#include <buola/gui.h>

#include <buola/algorithm/container.h>

#include <buola/image/cfont.h>
#include <buola/image/cgc.h>
#include <buola/gui/cicon.h>

#include <buola/widgets/crulerbox.h>
#include <buola/widgets/ctheme.h>

namespace buola { namespace gui {

CRulerBox::CRulerBox()
{
    SetStyle(STYLE_NONE);

    mDragging=false;
    mSorting=false;
    mInside=false;
    mSelected=-1;
    mUnder=-1;

    get_default_theme()->PrepareRulerBox(this);

    mFont=img::CFont::GetStock(L"control");
}

CRulerBox::~CRulerBox()
{
}

void CRulerBox::RemoveFields()
{
    mFields.clear();
}

void CRulerBox::AddField(TID pID,const std::wstring &pName,PIcon pIcon,double pSize)
{
    SField &lNew=construct_back(mFields);

    lNew.mID=pID;
    lNew.mName=pName;
    lNew.mIcon=pIcon;
    lNew.mSize=pSize;
    lNew.mHighlight=false;
    if(GetStyle(RBS_VERTICAL))
        lNew.mMinSize=4+mFont->GetHeight();
    else
        lNew.mMinSize=12+mFont->GetWidth(pName);

    if(mFields.size()>1)
    {
        lNew.mPos=mFields[mFields.size()-2]->mPos+mFields[mFields.size()-2]->mSize;
    }
    else
    {
        lNew.mPos=0;
    }
}

TID CRulerBox::GetIDAt(double pPos)
{
    int lItem=GetItemAt(CPoint_d(pPos,pPos)+mScrollPos);
    if(lItem<0) return -1;
    return mFields[lItem]->mID;
}

void CRulerBox::SetScroll(double pS)
{
    if(GetStyle(RBS_VERTICAL))
        mScrollPos.y=pS;
    else
        mScrollPos.x=pS;
    Refresh();
}

void CRulerBox::SetFieldName(int i,const std::wstring &pName)
{
    mFields[i]->mName=pName;

    if(GetStyle(RBS_VERTICAL))
        mFields[i]->mMinSize=4+mFont->GetHeight();
    else
        mFields[i]->mMinSize=12+mFont->GetWidth(pName);
}

void CRulerBox::HighlightNone()
{
    for(int i=0;i<mFields.size();i++)
    {
        mFields[i]->mHighlight=false;
    }
    Refresh();
}

void CRulerBox::HighlightField(TID pID,const img::CColor &pColor)
{
    for(int i=0;i<mFields.size();i++)
    {
        if(mFields[i]->mID==pID)
        {
            mFields[i]->mHighlight=true;
            mFields[i]->mHighlightColor=pColor;
        }
    }
    Refresh();
}

void CRulerBox::DrawField(img::CGC *pGC,const SField &pField,bool pUnder)
{
    CRect_d lRect;
    if(GetStyle(RBS_VERTICAL))
        lRect={0,pField.mPos-mScrollPos.y,
            Size().x,pField.mPos-mScrollPos.y+pField.mSize-1};
    else
        lRect={pField.mPos-mScrollPos.x,0,pField.mPos-mScrollPos.x+pField.mSize-1,Size().y};

    if(pField.mHighlight)
        get_default_theme()->DrawRulerFieldHL(pGC,lRect,pField.mName,pField.mHighlightColor,
                                                GetStyle(RBS_VERTICAL));
    else
        get_default_theme()->DrawRulerField(pGC,lRect,pField.mName,pUnder,
                                                GetStyle(RBS_VERTICAL));
}

int CRulerBox::GetEdgeAt(const CPoint_d &pP)
{
    double lPos=GetStyle(RBS_VERTICAL)?pP.y:pP.x;

    for(int i=0;i<mFields.size();i++)
    {
        if(fabs(lPos-mFields[i]->mPos)<3)
            return i;
    }
    if(mFields.size())
    {
        if(fabs(lPos-(mFields[mFields.size()-1]->mPos+
                    mFields[mFields.size()-1]->mSize))<3)
            return mFields.size();
    }

    return -1;
}

int CRulerBox::GetItemAt(const CPoint_d &pP)
{
    double lPos=GetStyle(RBS_VERTICAL)?pP.y:pP.x;

    for(int i=0;i<mFields.size();i++)
    {
        if(lPos<mFields[i]->mPos)
            return i-1;
    }
    return mFields.size()-1;
}

void CRulerBox::UpdatePointer()
{
    if(mSelected>=0)
    {
        if(GetStyle(RBS_VERTICAL))
            SetPointer(CPointer::STOCK_HSPLIT);
        else
            SetPointer(CPointer::STOCK_VSPLIT);
    }
    else
    {
        SetPointer(CPointer::STOCK_DEFAULT);
    }
}

///////////////// events //////////////////

void CRulerBox::OnExpose(const CExposeEvent &pE)
{
    img::CGC *lGC=pE.mGC;

    get_default_theme()->DrawRulerBox(lGC,this,CTheme::DRAW_NONE);

    for(int i=0;i<mFields.size();i++)
    {
        DrawField(lGC,*mFields[i],(mUnder==i));
    }
}

void CRulerBox::OnButtonDown(const CMouseEvent &pE)
{
    if(pE.mButton!=EMouseButton::LEFT) return;

    CPoint_d lP=pE.mPoint+mScrollPos;

    double lPos=GetStyle(RBS_VERTICAL)?lP.y:lP.x;
    double lRootPos=GetStyle(RBS_VERTICAL)?pE.mRootPoint.y:pE.mRootPoint.x;

    int lSelected=GetEdgeAt(lP);
    if(lSelected!=mSelected)
    {
        mSelected=lSelected;
        UpdatePointer();
    }

    if(mSelected>=0)
    {
        mDragging=true;
        mDragPos=lPos;
        mDragRootPos=lRootPos;
        mUnder=-2;
    }

    Refresh();
}

void CRulerBox::OnButtonUp(const CMouseEvent &pE)
{
    if(pE.mButton!=EMouseButton::LEFT) return;

    mDragging=false;
    mSorting=false;
}

void CRulerBox::OnMouseMove(const CMouseEvent &pE)
{
    CPoint_d lP=pE.mPoint+mScrollPos;

    double lPos=GetStyle(RBS_VERTICAL)?lP.y:lP.x;
    double lRootPos=GetStyle(RBS_VERTICAL)?pE.mRootPoint.y:pE.mRootPoint.x;

    if(mDragging)
    {
        if(lRootPos!=mDragRootPos)
        {
            double lChange=(int)(lPos-mDragPos);
            if(mSelected>0)
            {
                if(mFields[mSelected-1]->mSize+lChange<
                    mFields[mSelected-1]->mMinSize)
                {
                    lChange=mFields[mSelected-1]->mMinSize-
                            mFields[mSelected-1]->mSize;
                    lPos=mDragPos+lChange;
                }

                mFields[mSelected-1]->mSize+=lChange;

            }
            else
            {
                //we are dragging the first item, so it is the ruler itself
                eFirstMoved(lRootPos-mDragRootPos);
                mDragPos=lPos;
                mDragRootPos=lRootPos;
                return;
            }

            for(int i=mSelected;i<mFields.size();i++)
            {
                mFields[i]->mPos+=lChange;
            }

            eMoved(mFields[mSelected-1]->mID,mFields[mSelected-1]->mSize);

            Refresh();
        }
        mDragPos=lPos;
        mDragRootPos=lRootPos;
    }
    else if(!mSorting)
    {
        int lUnder=GetItemAt(lP);
        if(lUnder!=mUnder)
        {
            mUnder=lUnder;
            Refresh();
        }

        int lSelected=GetEdgeAt(lP);
        if(lSelected==mSelected) return;
        mSelected=lSelected;

        UpdatePointer();
    }
}

void CRulerBox::OnMouseIn(const CMouseEvent &pE)
{
    mInside=true;

    if(mSorting)
    {
        Refresh();
    }
}

void CRulerBox::OnMouseOut(const CMouseEvent &pE)
{
    mInside=false;

    if(mSorting)
    {
        Refresh();
    }
    if(mUnder>=-1)
    {
        mUnder=-2;
        Refresh();
    }
}

/*namespace gui*/ } /*namespace buola*/ }
